/*
 * Copyright 2016-2017 TVI Go Easy.
 * Created on 2017/8/23 09:04
 */
package com.gitee.mechanic.json.mapper;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.gitee.mechanic.json.module.DefaultSimpleModule;
import com.gitee.mechanic.json.serializer.NullToEmptySerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Map;

/**
 * 基于Jackson XmlMapper的重新封装的Xml转换工具类
 *
 * @author mechanic
 * @version 0.0.1
 */
@Slf4j
public class JsonMapper {

    public static final JsonMapper instance = new JsonMapper();

    private ObjectMapper mapper;

    public JsonMapper() {
        this(null);
    }

    public JsonMapper(JsonInclude.Include include) {
        this.mapper = new ObjectMapper();
        if (include != null) {
            this.mapper.setSerializationInclusion(include);
        }
        this.mapper
          //.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
          .configure(SerializationFeature.INDENT_OUTPUT, true)
          .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
          .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
          .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
          .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
          .configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true)
          .registerModule(new DefaultSimpleModule())
          .registerModule(new ParameterNamesModule())
          .registerModule(new Jdk8Module())
          .registerModule(new JavaTimeModule())
          .getSerializerProvider().setNullValueSerializer(new NullToEmptySerializer());
    }

    /**
     * 将bean转成json
     *
     * @param object 需要转换的对象
     * @return 转换后的json
     */
    public String toJson(Object object) {
        try {
            return this.mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            log.error("将对象转换成json时发生错误:" + object, e);
            return null;
        }
    }

    /**
     * 反序列化json到简单的对象
     *
     * @param content 字符串形式的json
     * @param clazz   输出的对象类型
     * @param <T> 返回的对象类型
     * @return 反序列化后的对象
     */
    public <T> T fromJson(String content, Class<T> clazz) {
        if (StringUtils.isEmpty(content)) {
            return null;
        }
        try {
            return this.mapper.readValue(content, clazz);
        } catch (IOException e) {
            log.error("将xml转换成对象时发生错误:" + clazz, e);
            return null;
        }
    }

    /**
     * 反序列化json到复杂的泛型对象
     *
     * @param content  字符串形式的json
     * @param javaType 输出的对象类型。
     *                 通过 {@link #buildType(Class, Class)} 或者 {@link #buildType(Class, Class, Class)} 构造
     * @param <T> 返回的对象类型
     * @return 反序列化后的对象
     */
    @SuppressWarnings("unchecked")
    public <T> T fromJson(String content, JavaType javaType) {
        if (StringUtils.isEmpty(content)) {
            return null;
        }
        try {
            return (T) this.mapper.readValue(content, javaType);
        } catch (IOException e) {
            log.error("将json转换成对象时发生错误:" + content, e);
            return null;
        }
    }

    /**
     * 反序列化流形式的json到简单的对象
     *
     * @param stream 流形式的json
     * @param clazz  输出的对象类型
     * @param <T> 返回的对象类型
     * @return 反序列化后的对象
     */
    public <T> T fromJson(InputStream stream, Class<T> clazz) {
        if (stream == null) {
            return null;
        }
        try {
            return this.mapper.readValue(stream, clazz);
        } catch (IOException e) {
            log.error("将Stream形式的json转换成对象时发生错误:" + clazz, e);
            return null;
        }
    }

    /**
     * 反序列化流形式的json到复杂的泛型对象
     *
     * @param stream   流形式的json
     * @param javaType 输出的对象类型。
     *                 通过 {@link #buildType(Class, Class)} 或者 {@link #buildType(Class, Class, Class)} 构造
     * @param <T> 返回的对象类型
     * @return 反序列化后的对象
     */
    public <T> T fromJson(InputStream stream, JavaType javaType) {
        if (stream == null) {
            return null;
        }
        try {
            return this.mapper.readValue(stream, javaType);
        } catch (IOException e) {
            log.error("将Stream形式的json转换成对象时发生错误:" + javaType, e);
            return null;
        }
    }

    /**
     * 反序列化流形式的json到复杂的泛型对象
     *
     * @param content        流形式的json
     * @param typeReference 输出的对象类型
     * @param <T> 返回的对象类型
     * @return 反序列化后的对象
     */
    public <T> T fromJson(String content, TypeReference typeReference) {
        if (StringUtils.isEmpty(content)) {
            return null;
        }
        try {
            return this.mapper.readValue(content, typeReference);
        } catch (IOException e) {
            log.error("将json转换成对象时发生错误:" + typeReference, e);
            return null;
        }
    }

    /**
     * 反序列化流形式的json到复杂的泛型对象
     *
     * @param stream        流形式的json
     * @param typeReference 输出的对象类型
     * @param <T> 返回的对象类型
     * @return 反序列化后的对象
     */
    public <T> T fromJson(InputStream stream, TypeReference typeReference) {
        if (stream == null) {
            return null;
        }
        try {
            return this.mapper.readValue(stream, typeReference);
        } catch (IOException e) {
            log.error("将Stream形式的json转换成对象时发生错误:" + typeReference, e);
            return null;
        }
    }

    /**
     * 构造带泛型的集合类型
     *
     * @param collectionClass 继承Collection接口的对象。如Map，HashMap，LinkedHashMap等。
     * @param elementClass    积累的泛型对象
     * @return 集合形式的JavaType
     */
    public JavaType buildType(Class<? extends Collection> collectionClass, Class<?> elementClass) {
        return this.mapper.getTypeFactory().constructCollectionType(collectionClass, elementClass);
    }

    /**
     * 构造Map类型
     *
     * @param mapClass   继承Map接口的对象。如Map，HashMap，LinkedHashMap等。
     * @param keyClass   Map键的类型对象
     * @param valueClass Map值的类型对象
     * @return Map形式的JavaType
     */
    public JavaType buildType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
        return this.mapper.getTypeFactory().constructMapType(mapClass, keyClass, valueClass);
    }

    /**
     * 获取原始的ObjectMapper
     *
     * @return 原始的ObjectMapper
     */
    public ObjectMapper getMapper() {
        return mapper;
    }

}
